#include <pthread.h>
#include <stdio.h>
#include <utils_ptrlist.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include <utils_file.h>
#include <utils_net.h>
#include <longtime.h>
#include <utils_daemon.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include "../include/app.h"
#include "utils_ptrlist.h"
#include "tcpproxy.h"

#define max(a, b) (a > b ? a : b)
#define Sleep(n) usleep(n * 1000)

int has_gone = 0;
void trace_log(const char *fmt, ...);
void lprintf(const char *fmt, ...);

typedef struct tagProxy
{
	int enable;
	int sock_dev;
	int sock_cloud;
	int device;
} ProxyNode;

static int bQuit = 0;
static pthread_t proxy;
static PtrList ClientList = PTRLIST_INITIALIZER; // 客户队列
static pthread_mutex_t client_lock;				 // 客户队列操作锁，因为客户维护都是在该进程中操作的也可以不要

#define CLIENT_LOCK() pthread_mutex_lock(&client_lock)
#define CLIENT_UNLOCK() pthread_mutex_unlock(&client_lock)

// 转发线程，我们将架线tcp数据另外开设线程，防止主线程控制方式的阻塞调用耽误数据的转发
void *forward_thread(void *arg)
{
	// ProxyNode *obj = NULL;
	int sock_dev, sock_cloud;
	POSITION pos;
	int fd_max = 0;
	char buffer[4096] = {0};
	while (!bQuit)
	{
		if (ClientList.count == 0)
		{
			Sleep(10);
			continue;
		}
		if (has_gone == 1)
		{
			CLIENT_LOCK();
			for (pos = ClientList.head; pos != NULL; pos = pos->next)
			{
				ProxyNode *obj = (ProxyNode *)pos->ptr;
				if (obj->enable == 0)
				{
					obj = PtrList_remove(&ClientList, pos);
					if (obj->sock_cloud > 0)
						close(obj->sock_cloud);
					if (obj->sock_dev > 0)
						close(obj->sock_dev);
					free(obj);
					trace_log("delete sock pair ..\r\n");
					pos = ClientList.head;
					if (pos == NULL)
						break;
				}
			}
			CLIENT_UNLOCK();
		}
		fd_set fs;
		struct timeval tv;
		int ret;
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		FD_ZERO(&fs);
		CLIENT_LOCK();
		for (pos = ClientList.head; pos != NULL; pos = pos->next)
		{
			ProxyNode *obj = (ProxyNode *)pos->ptr;
			if (!obj->enable)
				continue;
			fd_max = fd_max > obj->sock_dev ? fd_max : obj->sock_dev;
			FD_SET(obj->sock_dev, &fs);
			fd_max = fd_max > obj->sock_cloud ? fd_max : obj->sock_cloud;
			FD_SET(obj->sock_cloud, &fs);
		}
		CLIENT_UNLOCK();
		ret = select(fd_max + 1, &fs, NULL, NULL, &tv);
		has_gone = 0;
		if (ret > 0)
		{
			for (pos = ClientList.head; pos != NULL; pos = pos->next)
			{
				ProxyNode *obj = (ProxyNode *)pos->ptr;
				if (!obj->enable)
					continue;
				if (obj->sock_dev > 0 && FD_ISSET(obj->sock_dev, &fs))
				{
					int len = sock_read(obj->sock_dev, (char *)buffer, sizeof(buffer));
					if (len <= 0)
					{
						sock_close(obj->sock_dev);
						obj->sock_dev = -1;
						sock_close(obj->sock_cloud);
						obj->sock_cloud = -1;
						trace_log("设备端连接已经断开..\r\n");
						obj->enable = 0;
						has_gone = 1;
						continue;
					}
					if (obj->sock_cloud > 0)
						sock_write_n_bytes(obj->sock_cloud, buffer, len);
				}
				if (obj->sock_cloud > 0 && FD_ISSET(obj->sock_cloud, &fs))
				{
					int len = sock_read(obj->sock_cloud, (char *)buffer, sizeof(buffer));
					if (len <= 0)
					{
						trace_log("客户端连接已经断开..\r\n");
						sock_close(obj->sock_dev);
						obj->sock_dev = -1;
						sock_close(obj->sock_cloud);
						obj->sock_cloud = -1;
						obj->enable = 0;
						has_gone = 1;
						continue;
					}
					sock_write_n_bytes(obj->sock_dev, buffer, len);
				}
			}
		}
	}
	return NULL;
}

void ProxyAddClient(int fd_br, int fd_serv)
{
	ProxyNode *node = malloc(sizeof(ProxyNode));
	memset(node, 0, sizeof(ProxyNode));
	node->sock_dev = fd_br;
	node->sock_cloud = fd_serv;
	node->enable = 1;
	CLIENT_LOCK();
	PtrList_append(&ClientList, node);
	CLIENT_UNLOCK();
	return 0;
}

void ProxyDropAllClient()
{
	CLIENT_LOCK();
	POSITION pos = ClientList.head;
	while (pos)
	{
		ProxyNode *node = (ProxyNode *)pos->ptr;
		node->enable = 0;
		pos = pos->next;
	}
	has_gone = 1;
	CLIENT_UNLOCK();
}

void ProxyStop()
{
	bQuit = 1;
}

void ProxyStart()
{
	forward_thread(NULL);
}
